package com.cmc.cloud.cmclink.doc.api.document;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.api.document.rep.*;
import com.cmc.cloud.cmclink.doc.api.document.req.AttachmentUpdateReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.ImExActualLoadingReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.RequirementsUpdateReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.si.*;
import com.cmc.cloud.cmclink.doc.api.document.req.so.SoForecastBaseVO;
import com.cmc.cloud.cmclink.doc.api.document.req.so.SoForecastRespVO;
import com.cmc.cloud.cmclink.doc.constant.DocApiConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoCntrErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.SoErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.*;
import com.cmc.cloud.cmclink.doc.dto.CntrTeuDTO;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.CntrTransferService;
import com.cmc.cloud.cmclink.doc.service.BlnoCntrService;
import com.cmc.cloud.cmclink.doc.service.si.*;
import com.cmc.cloud.cmclink.doc.service.so.BizSoService;
import com.cmc.cloud.cmclink.doc.service.so.SoSplitCombineService;
import com.cmc.cloud.cmclink.doc.vo.blnoblprintlogvo.BlnoBlPrintLogListByBlnoIdReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnoblprintlogvo.BlnoMaintenanceInfoRespVO;
import com.cmc.cloud.cmclink.doc.vo.blnocargovo.BlnoCargoUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnocntrvo.BlnoCntrUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnopaymentvo.BlnoPaymentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnoscnvo.BlnoScnUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoBaseInfoVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoImPageReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoImPageRespVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoInfoVO;
import com.cmc.cloud.cmclink.doc.vo.blnovslvoyvo.BlnoVslvoyUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.requirementsvo.RequirementsUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.socargovo.SoCargoRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoDetailRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovslvoyvo.SoVslvoyRespVO;
import com.cmc.cloud.cmclink.doc.vo.vgm.VgmCreateReqVO;
import com.cmc.cloud.cmclink.eqm.api.model.request.CommonCntrTypeQueryParam;
import com.cmc.cloud.cmclink.eqm.api.model.response.CommonCntrTypeDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.CommonResult;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.cmc.cloud.cmclink.doc.api.document.emums.SoStatusEnum.BookingConfirmed;
import static com.cmc.cloud.cmclink.doc.constants.errorconstant.SoErrorCodeConstants.WEB_SO_VOYAGE_LOCK_ERROR;


/**
 * SI信息创建
 *
 * @author stq
 * @date 2025/7/7
 */
@Slf4j
@RestController
@Valid
public class SiInfoApiImpl implements SiInfoApi {

    @Resource
    private BlnoService blnoService;
    @Resource
    private BlnoCntrService blnoCntrService;
    @Resource
    private CntrTransferService cntrTransferService;
    @Resource
    private BlnoBlPrintLogService blnoBlPrintLogService;
    @Resource
    private SoMapper soMapper;
    @Resource
    private BlnoMapper blnoMapper;
    @Resource
    private BlnoSpiltCombineService blnoSpiltCombineService;
    @Resource
    private BlnoScnMapper blnoScnMapper;
    @Resource
    private BlnoCntrMapper blnoCntrMapper;
    @Resource
    private BizSoService bizSoService;
    @Resource
    private BlnoSpiltCombineDetailService blnoSpiltCombineDetailService;
    @Resource
    private BlnoSpiltCombineDetailMapper blnoSpiltCombineDetailMapper;
    @Resource
    private VoyageLockService voyageLockService;
    @Resource
    private SoVslvoyMapper soVslvoyMapper;
    @Resource
    private BlnoSpiltCombineMapper blnoSpiltCombineMapper;
    @Resource
    private ConfirmNoticeMapper confirmNoticeMapper;
    @Resource
    private ConfirmNoticeDetailMapper confirmNoticeDetailMapper;
    @Resource
    private BlnoScnService blnoScnService;
    @Resource
    private BlnoVslvoyService blnoVslvoyService;
    @Resource
    private SoSplitCombineService soSplitCombineService;
    @Resource
    private BlnoMultipleSoMapper blnoMultipleSoMapper;

    /**
     * SI可以拆分和合并的SI状态
     */
    private final static List<String> CAN_SPLIT_AND_MERGE_SI_STATUS = Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue()
            , SiStatusEnum.AwaitingMaterialUploaded.getValue(), SiStatusEnum.BookingConfirmed.getValue());
    /**
     * SI可以合并的货类
     */
    private final static List<String> CAN_MERGE_CAGO_TYPES = Arrays.asList("DRY", "FQ", "AK", "DG");

    private final static List<String> AFTER_SI_CONFIRM = Arrays.asList("SI03", "SI04", "SI05", "SI06");

    @Override
    public CommonResult<PageResult<BlnoPageRespDto>> queryBlnoInfoPage(BlnoPageReqDto blnoPageReqDto) {
        IPage<BlnoPageRespDto> page = Page.of(blnoPageReqDto.getPageNo(), blnoPageReqDto.getPageSize());

        //CreateDateStart CreateDateEnd 格式为yyyy-MM-dd
        //修正：CreateDateStart CreateDateEnd
        if (StringUtils.isNotBlank(blnoPageReqDto.getCreateDateStart())) {
            blnoPageReqDto.setCreateDateStart(blnoPageReqDto.getCreateDateStart() + " 00:00:00");
        }
        if (StringUtils.isNotBlank(blnoPageReqDto.getCreateDateEnd())) {
            blnoPageReqDto.setCreateDateEnd(blnoPageReqDto.getCreateDateEnd() + " 23:59:59");
        }
        // 格式化SiChangeCutOff时间
        if (StringUtils.isNotBlank(blnoPageReqDto.getSiChangeCutOffStart())) {
            blnoPageReqDto.setSiChangeCutOffStart(blnoPageReqDto.getSiChangeCutOffStart() + " 00:00:00");
        }
        if (StringUtils.isNotBlank(blnoPageReqDto.getSiChangeCutOffEnd())) {
            blnoPageReqDto.setSiChangeCutOffEnd(blnoPageReqDto.getSiChangeCutOffEnd() + " 23:59:59");
        }
        IPage<BlnoPageRespDto> pageResult = blnoService.rpcPageBlnoInfo(page, blnoPageReqDto);
        List<BlnoPageRespDto> blnoPageRespDtoList = pageResult.getRecords();

        if (CollectionUtils.isEmpty(blnoPageRespDtoList)) {
            return CommonResult.success(new PageResult<>(Collections.emptyList(), pageResult.getTotal()));
        }

        List<Long> blnoIds = blnoPageRespDtoList.stream().map(BlnoPageRespDto::getBlnoId).collect(Collectors.toList());
        //发货人信息
        Map<Long, List<BlnoScnRespDto>> blnoScnMap = queryBlnoScn(blnoIds);
        //箱信息
        Map<Long, List<BlnoCntrUpdateReqDto>> blnoCntrsMap = queryBlnoCntr(blnoIds);
        //订舱信息
        Map<String, SoDO> soMap = querySoInfoByBookingNo(blnoPageRespDtoList.stream().map(BlnoPageRespDto::getBookingNo).collect(Collectors.toList()));
        // 航线数据
        Map<Long, List<SoVslvoyDO>> soVslvoyMap;
        if(!soMap.isEmpty()){
            soVslvoyMap = querySoVslvoyInfo(soMap);
        } else {
            soVslvoyMap = new HashMap<>(16);
        }
        //合并单
        Map<Long, Long> combineBnlnoMap = queryCombineBlno(blnoIds);
        List<BlnoDO> blnoList = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>()
                .eq(BlnoDO::getValid, SiValidEnum.PENDING.getValue())
                .inIfPresent(BlnoDO::getId, blnoIds));
        Map<Long, BlnoDO> blnoMap = blnoList.stream().filter(Objects::nonNull).collect(Collectors.toMap(BlnoDO::getId, e -> e, (e1, e2) -> e1));
        blnoPageRespDtoList.forEach(entity -> {
            //箱信息
            entity.setBlnoCntrs(blnoCntrsMap.getOrDefault(entity.getBlnoId(), Collections.emptyList()));
            //发货人信息
            entity.setShipperList(blnoScnMap.getOrDefault(entity.getBlnoId(), Collections.emptyList()));
            SoDO so = soMap.getOrDefault(entity.getBookingNo(), null);
            entity.setSoId(Objects.isNull(so) ? null : so.getId());
            if (so != null) {
                List<SoVslvoyDO> soVslvoyDoList = soVslvoyMap.get(so.getId());
                checkVslvoyLock(soVslvoyDoList, entity);
            }
            //合并完的提单ID
            entity.setCombineBlnoId(combineBnlnoMap.getOrDefault(entity.getBlnoId(), null));
            // 设置拆并更改状态
            BlnoSpiltCombineDO combineDO = blnoSpiltCombineDetailService.getSpiltCombineAudit(entity);
            if(Objects.nonNull(combineDO)){
                entity.setOperationType(combineDO.getOperationType());
                entity.setApproveStatus(combineDO.getApproveStatus());
            }
            entity.setCanChangeFlag(checkCanUpdate(entity, blnoMap));
            //是否可拆分
            entity.setCanSplit(isAllowSplit(entity));
            //是否可以合并
            entity.setCanCombine(isAllowCombine(entity));
            //判断是否北方四港
            entity.setManifestFlag(soSplitCombineService.checkPortAgent(entity.getPolCode(),entity.getPorCode(),entity.getTopAgentCode()));
        });
        /*checkSiCanSplitAndCombine(blnoPageRespDtoList);*/
        return CommonResult.success(new PageResult<>(blnoPageRespDtoList, pageResult.getTotal()));
    }

    /**
     * 校验是否锁船
     *
     * @param soVslvoyList 航线数据
     * @param entity       entity
     */
    private void checkVslvoyLock(List<SoVslvoyDO> soVslvoyList, BlnoPageRespDto entity) {
        if (CollectionUtil.isNotEmpty(soVslvoyList)) {
            for (SoVslvoyDO e : soVslvoyList) {
                if (TransmodeEnum.LINER.getValue().equals(e.getTransmode())) {
                    //航次锁船记录
                    VoyageLockDO voyageLock = voyageLockService.findVoyageLock(e.getVesselCode(), e.getVoyageEx(), e.getPolCode(), ExImTypeEnum.EX.getValue());
                    if (voyageLock != null && voyageLock.getVoyageLockFlag()) {
                        entity.setBlShipperLockFlag(Boolean.TRUE);
                        return;
                    }
                }
            }
            entity.setBlShipperLockFlag(Boolean.FALSE);
        }
    }

    /**
     * 查询合并中的提单ID信息
     *
     * @param blnoIds
     * @return
     */
    private Map<Long, Long> queryCombineBlno(List<Long> blnoIds) {
        Map<Long, Long> combineBnlnoMap = new HashMap<>(16);

        List<BlnoSpiltCombineDetailDO> blnoSpiltCombineDetails = blnoSpiltCombineDetailMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>()
                .inIfPresent(BlnoSpiltCombineDetailDO::getBlnoId, blnoIds));
        if (CollectionUtils.isEmpty(blnoSpiltCombineDetails)) {
            return combineBnlnoMap;
        }
        List<Long> operationIds = blnoSpiltCombineDetails.stream().map(BlnoSpiltCombineDetailDO::getOperationId).collect(Collectors.toList());
        List<BlnoSpiltCombineDO> blnoSpiltCombines = blnoSpiltCombineMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDO>()
                .inIfPresent(BlnoSpiltCombineDO::getId, operationIds)
                .eqIfPresent(BlnoSpiltCombineDO::getOperationType, BlnoSpiltCombineTypeEnum.COMBINE.getValue())
                .eqIfPresent(BlnoSpiltCombineDO::getApproveStatus, SiSplitCombineApproveStatusEnum.PENDING.getValue()));
        if (CollectionUtils.isEmpty(blnoSpiltCombines)) {
            return combineBnlnoMap;
        }
        Set<Long> comBineApplyIds = blnoSpiltCombines.stream().map(BlnoSpiltCombineDO::getId).collect(Collectors.toSet());
        blnoSpiltCombineDetails.removeIf(e -> !comBineApplyIds.contains(e.getOperationId()));

        combineBnlnoMap = blnoSpiltCombineDetails.stream().collect(Collectors.toMap(BlnoSpiltCombineDetailDO::getBlnoId, BlnoSpiltCombineDetailDO::getNewBlnoId, (o, o1) -> o));
        return combineBnlnoMap;

    }
    @Override
    public CommonResult<BlnoContainersStatisticsRespDto> queryBlnoContainersInfo(BlnoPageReqDto blnoPageReqDto) {

        BlnoContainersStatisticsRespDto blnoContainersStatisticsRespDto = new BlnoContainersStatisticsRespDto();

        //CreateDateStart CreateDateEnd 格式为yyyy-MM-dd
        //修正：CreateDateStart CreateDateEnd
        if (StringUtils.isNotBlank(blnoPageReqDto.getCreateDateStart())) {
            blnoPageReqDto.setCreateDateStart(blnoPageReqDto.getCreateDateStart() + " 00:00:00");
        }
        if (StringUtils.isNotBlank(blnoPageReqDto.getCreateDateEnd())) {
            blnoPageReqDto.setCreateDateEnd(blnoPageReqDto.getCreateDateEnd() + " 23:59:59");
        }
        // 格式化SiChangeCutOff时间
        if (StringUtils.isNotBlank(blnoPageReqDto.getSiChangeCutOffStart())) {
            blnoPageReqDto.setSiChangeCutOffStart(blnoPageReqDto.getSiChangeCutOffStart() + " 00:00:00");
        }
        if (StringUtils.isNotBlank(blnoPageReqDto.getSiChangeCutOffEnd())) {
            blnoPageReqDto.setSiChangeCutOffEnd(blnoPageReqDto.getSiChangeCutOffEnd() + " 23:59:59");
        }

        int teu = 0;
        Map<String, Integer> containerMap = new HashMap<>(16);


        List<BlnoCntrStaticDto> cntrStaticDtos = blnoService.rpcBlnoCntr(blnoPageReqDto)
                .stream().filter(c -> ObjectUtil.isNotEmpty(c.getCntrSize()) && ObjectUtil.isNotEmpty(c.getCntrType()))
                .collect(Collectors.toList());

        // 修复：只有当cntrStaticDtos不为空时才进行计算
        if (CollectionUtils.isNotEmpty(cntrStaticDtos)) {

            for (BlnoCntrStaticDto cntrStaticDto : cntrStaticDtos) {
                int cntrTeu = Integer.parseInt(cntrStaticDto.getCntrSize()) / 20;
                teu = teu + cntrTeu;
            }
            // 20GP*3;40GP*6
            containerMap.putAll(cntrStaticDtos.stream()
                    .collect(Collectors.groupingBy(
                            c -> c.getCntrSize() + c.getCntrType(),
                            Collectors.collectingAndThen(Collectors.toList(), List::size)
                    )));
        }

        //fixme??teu=0??cause 20HH:teu=0  teu:0,containerInfo:20HH*2;20HR*1
        blnoContainersStatisticsRespDto.setTeu(teu);
        blnoContainersStatisticsRespDto.setContainerMap(containerMap);
        return CommonResult.success(blnoContainersStatisticsRespDto);
    }

    private List<CntrTeuDTO> selectCntrTeu(List<String> sizeTypeList) {
        CommonCntrTypeQueryParam param = new CommonCntrTypeQueryParam();
        param.setSizeTypeList(sizeTypeList);
        List<CommonCntrTypeDTO> commonCntrTypeDTOList = cntrTransferService.getCntrTypeList(param);
        return ApproveSpaceConvert.INSTANCE.convertCntrList(commonCntrTypeDTOList);
    }

    @Override
    public CommonResult<BlnoStatisticsRespDto> getBlnoStatistics(String csmCode,String creator) {
        return CommonResult.success(blnoService.getBlnoStatistics(csmCode,creator));
    }

    @Override
    public CommonResult<BlnoInfoDto> getBlnoDetail(BlnoDetailReqDto blnoDetailReqDto) {
        if (blnoDetailReqDto.getBlnoId() == null && blnoDetailReqDto.getBookingNo() == null && blnoDetailReqDto.getBlNo() == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        BlnoDO blnoDO = blnoService.getBlno(blnoDetailReqDto);

        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        BlnoInfoVO blnoInfoVO = blnoService.getSiDetail(blnoDO.getId());
        BlnoInfoDto blnoInfoDto = new BlnoInfoDto();
        //基础信息
        BlnoBaseInfoDto blnoBaseInfo = BlnoConvert.INSTANCE.convertBlnoBaseInfoDto(blnoInfoVO.getBlnoBasInfoVO());
        SoDO soDO = soMapper.selectOne(new LambdaQueryWrapper<SoDO>()
                .eq(SoDO::getBlNo, blnoDO.getBlNo())
                .eq(SoDO::getBookingNo, blnoDO.getBookingNo())
                .last("limit 1")
        );
        if (soDO != null) {
            blnoBaseInfo.setSoId(soDO.getId());
        }

        blnoInfoDto.setBlnoBasInfoVO(blnoBaseInfo);
        //换单数据
        try {
            BlnoImPageReqVO reqVO = new BlnoImPageReqVO();
            reqVO.setBlNo(blnoDO.getBlNo());
            List<BlnoImPageRespVO> exportList = blnoService.exportBlnoIm(reqVO);
            if (CollectionUtil.isNotEmpty(exportList)) {
                blnoInfoDto.getBlnoBasInfoVO().setDoDate(exportList.get(exportList.size() - 1).getDoDate());
            }
        } catch (Exception e) {
            log.error("查询换单数据异常", e);
        }
        //航线信息
        List<BlnoVslvoyUpdateReqDto> blnoVslvoys = BlnoVslvoyConvert.INSTANCE.convertBlnoVslvoyUpdateReqDto(blnoInfoVO.getBlnoVslvoys());
        blnoService.setShippingScheduleTime(blnoVslvoys);
        blnoInfoDto.setBlnoVslvoys(blnoVslvoys);
        //支付信息
        List<BlnoPaymentUpdateReqDto> blnoPayments = BlnoPaymentConvert.INSTANCE.convertBlnoPaymentUpdateReqDto(blnoInfoVO.getBlnoPayments());
        blnoInfoDto.setBlnoPayments(blnoPayments);
        //附加要求
        List<RequirementsUpdateReqDto> reqList = RequirementsConvert.INSTANCE.convertRequirementsUpdateReqVO(blnoInfoVO.getReqVOList());
        blnoInfoDto.setReqVOList(reqList);
        //额外要求
        List<AttachmentUpdateReqDto> attachmentReqs = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqVO(blnoInfoVO.getAttachmentReqVO());
        blnoInfoDto.setAttachmentReqVO(attachmentReqs);
        //常用收发通
        List<BlnoScnUpdateReqDto> blnoScnUpdateReqs = BlnoScnConvert.INSTANCE.convertBlnoScnUpdateReqDtoVo(blnoInfoVO.getBlnoScns());
        blnoInfoDto.setBlnoScns(blnoScnUpdateReqs);
        //箱信息
        List<BlnoCntrUpdateReqDto> blnoCntrs = BlnoCntrConvert.INSTANCE.convertBlnoCntrUpdateReqDto(blnoInfoVO.getBlnoCntrs());
        blnoInfoDto.setBlnoCntrs(blnoCntrs);

        //货物信息
        List<BlnoCargoUpdateReqDto> blnoCargos = BlnoCargoConvert.INSTANCE.convertBlnoCargoUpdateReqDto(blnoInfoVO.getBlnoCargos());
        blnoInfoDto.setBlnoCargos(blnoCargos);
        //多so的预配箱信息
        blnoInfoDto.setSoForecastList(blnoService.getSoForecastBySoId(blnoBaseInfo.getId(),blnoBaseInfo.getBlNo()));
        return CommonResult.success(blnoInfoDto);
    }

    @Override
    public CommonResult<Long> createSiInfo(BlnoInfoDto blnoInfoDto) {
        log.info("rpcCreateSiInfo blnoInfoDto:{}", blnoInfoDto);
        BlnoInfoVO blnoInfoVO = new BlnoInfoVO();
        VoyageLockDO voyageLock;
        if(Objects.isNull(blnoInfoDto.getBlnoBasInfoVO().getId())){
            SoDO soDO = soMapper.selectOne(new LambdaQueryWrapper<SoDO>()
                    .eq(SoDO::getBookingNo, blnoInfoDto.getBlnoBasInfoVO().getBookingNo())
                    .eq(SoDO::getValidStatus,ValidStatusEnum.DEFAULT_VALID.getValue()));
            voyageLock = voyageLockService.findVoyageLock(soDO.getBookingVessel(),soDO.getBookingVoyage(),soDO.getPolCode(),ExImTypeEnum.EX.getValue());
        } else {
            BlnoDO blnoDO = blnoMapper.selectOne(new LambdaQueryWrapper<BlnoDO>()
                    .eq(BlnoDO::getId, blnoInfoDto.getBlnoBasInfoVO().getId()));
            voyageLock = voyageLockService.findVoyageLock(blnoDO.getBkgVesselCode(),blnoDO.getBkgVoyage(),blnoDO.getPolCode(),ExImTypeEnum.EX.getValue());
        }
        // SO关联的第一程LINER的船名航次+装货港已锁船
        if(Objects.nonNull(voyageLock) && voyageLock.getVoyageLockFlag()){
            throw ServiceExceptionUtil.exception(WEB_SO_VOYAGE_LOCK_ERROR);
        }
//        //提单维护-北方四港
//        boolean portAgentFlag = soSplitCombineService.checkPortAgent(blnoInfoDto.getBlnoBasInfoVO().getPolCode(),blnoInfoDto.getBlnoBasInfoVO().getPorCode(),blnoInfoDto.getBlnoBasInfoVO().getTopAgentCode());
//        if(portAgentFlag && Objects.isNull(blnoInfoDto.getManifestId())){
//            throw ServiceExceptionUtil.exception(WEB_SO_MANIFEST_ERROR);
//        }
        //基础信息
        BlnoBaseInfoVO blnoBaseInfoVO = BlnoConvert.INSTANCE.convertBlnoBaseInfoVO(blnoInfoDto.getBlnoBasInfoVO());
        blnoBaseInfoVO.setDatasource(blnoInfoDto.getDataSource());
        blnoInfoVO.setBlnoBasInfoVO(blnoBaseInfoVO);
        //航线信息
        List<BlnoVslvoyUpdateReqVO> blnoVslvoys = BlnoVslvoyConvert.INSTANCE.convertBlnoVslvoyUpdateReqVO(blnoInfoDto.getBlnoVslvoys());
        blnoInfoVO.setBlnoVslvoys(blnoVslvoys);
        //支付信息
        List<BlnoPaymentUpdateReqVO> blnoPayments = BlnoPaymentConvert.INSTANCE.convertBlnoPaymentUpdateReqVo(blnoInfoDto.getBlnoPayments());
        blnoInfoVO.setBlnoPayments(blnoPayments);
        //附加要求
        List<RequirementsUpdateReqVO> reqVOList = RequirementsConvert.INSTANCE.convertRequirementsUpdateReqDto(blnoInfoDto.getReqVOList());
        blnoInfoVO.setReqVOList(reqVOList);
        //额外要求
        List<AttachmentUpdateReqVO> attachmentReqVO = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqDto(blnoInfoDto.getAttachmentReqVO());
        blnoInfoVO.setAttachmentReqVO(attachmentReqVO);
        //常用收发通
        List<BlnoScnUpdateReqVO> blnoScnUpdateReqVos = BlnoScnConvert.INSTANCE.convertBlnoScnUpdateReqVO(blnoInfoDto.getBlnoScns());
        blnoInfoVO.setBlnoScns(blnoScnUpdateReqVos);
        //箱信息
        List<BlnoCntrUpdateReqVO> blnoCntrs = BlnoCntrConvert.INSTANCE.convertBlnoCntrUpdateReqVO(blnoInfoDto.getBlnoCntrs());
        blnoInfoVO.setBlnoCntrs(blnoCntrs);
        //货物信息
        List<BlnoCargoUpdateReqVO> blnoCargos = BeanUtil.copyToList(blnoInfoDto.getBlnoCargos(), BlnoCargoUpdateReqVO.class);
        blnoInfoVO.setBlnoCargos(blnoCargos);
        //vgm信息
        List<BlnoVgmReqDto> vgmCreateReqDtos = blnoInfoDto.getVgmCreateReqDtos();
        if (CollectionUtils.isNotEmpty(vgmCreateReqDtos)) {
            List<VgmCreateReqVO> vgmCreates = BeanUtil.copyToList(vgmCreateReqDtos, VgmCreateReqVO.class);
            blnoInfoVO.setVgmCreateReqVos(vgmCreates);
        }

        Long orUpdateSi = 0L;
        if (SiSendTypeEnum.DRAFT.getValue().equals(blnoInfoDto.getSiSubType())) {
            if (ObjectUtil.isNull(blnoBaseInfoVO.getId())) {
                orUpdateSi =
                        blnoService.createOrUpdateSi(blnoInfoVO, false, blnoInfoDto.getBlnoBasInfoVO().getCreator());
            } else {
                orUpdateSi =
                        blnoService.createOrUpdateSi(blnoInfoVO, true, blnoInfoDto.getBlnoBasInfoVO().getCreator());
            }
        } else {
            //提交校验是否是拼箱货
            lclCheck(blnoCntrs, blnoBaseInfoVO.getBkgVesselCode(), blnoBaseInfoVO.getBkgVoyage());
            blnoService.commitSi(blnoInfoVO, blnoInfoDto.getBlnoBasInfoVO().getCreator());
            orUpdateSi = blnoInfoVO.getBlnoBasInfoVO().getId();
        }
        return CommonResult.success(orUpdateSi);
    }

    @Override
    public CommonResult<Long> createSiInfo(@Valid List<BlnoEdiConfirmDto> blnoEdiConfirmDtos) {
        return null;
    }


    @Override
    public CommonResult<Boolean> draftConfirm(DraftConfirmDto draftConfirmDto) {
        if (ObjectUtils.isEmpty(draftConfirmDto.getConfirmer()) || ObjectUtils.isEmpty(draftConfirmDto.getConfirmDate())) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_PARAM_ERROR);
        }
        return CommonResult.success(blnoService.draftConfirm(draftConfirmDto));
    }

    @Override
    public CommonResult<BlnoMaintenanceInfoRespDto> billOfLadingPreview(DraftConfirmDto draftConfirmDto) {
        BlnoMaintenanceInfoRespVO blnoMaintenanceInfoRespVo = blnoBlPrintLogService.getBlnoMaintenanceInfo(draftConfirmDto.getBlId(), draftConfirmDto.getPrintType());
        BlnoMaintenanceInfoRespDto blnoMaintenanceInfoRespDto = BlnoConvert.INSTANCE.convertBlnoMaintenanceInfoRespVO(blnoMaintenanceInfoRespVo);
        if (!ObjectUtils.isEmpty(blnoMaintenanceInfoRespVo.getAttachment())) {
            blnoMaintenanceInfoRespDto.setAttachment(BeanUtil.copyProperties(blnoMaintenanceInfoRespVo.getAttachment(), BlnoPrintAttachmentRespDto.class));
        }
        return CommonResult.success(blnoMaintenanceInfoRespDto);
    }

    @Override
    public CommonResult<byte[]> printEdit(PrintEditDto printEditDto) {
        byte[] bytes = blnoBlPrintLogService.pdfCreateAnEdit(BlnoConvert.INSTANCE.convertPrintEditDto(printEditDto));
        return CommonResult.success(bytes);
    }

    @Override
    public CommonResult<BillsHistoricalRecordsDto> printDetails(DraftConfirmDto draftConfirmDto) {
        BlnoBlPrintLogListByBlnoIdReqVO blnoBlPrintLogListByBlnoIdReqVO = new BlnoBlPrintLogListByBlnoIdReqVO();
        blnoBlPrintLogListByBlnoIdReqVO.setBlnoId(draftConfirmDto.getBlId());
        List<BlnoBlPrintLogDO> blnoBlPrintLogPage = blnoBlPrintLogService.billsHistoricalRecords(blnoBlPrintLogListByBlnoIdReqVO);
        List<BillsHistoricalRecordsDto> billsHistoricalRecordsDtos = BlnoBlPrintLogConvert.INSTANCE.convertBillsHistoricalRecordsDto(blnoBlPrintLogPage);
        if (billsHistoricalRecordsDtos == null || billsHistoricalRecordsDtos.isEmpty()) {
            return CommonResult.success(new BillsHistoricalRecordsDto());
        }
        return CommonResult.success(billsHistoricalRecordsDtos.get(0));
    }

    @Override
    public CommonResult<Void> deleteBlno(Long blnoId) {
        BlnoDO blno = blnoService.getBlno(blnoId);
        if (blno == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        if (!Objects.equals(blno.getSiStatus(), SiStatusEnum.BookingDraft.getValue())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_INFO_DELETE_ERROR);
        }
        blnoMapper.deleteById(blnoId);
        return CommonResult.success();
    }

    @Override
    public CommonResult<Void> robackDraft(Long blnoId) {
        blnoService.returnDraft(blnoId);
        return CommonResult.success();
    }

    /**
     * 判断是否可以拆分或者合并
     *
     * @param blnoPageRespDtoList si分页列表
     */
    private void checkSiCanSplitAndCombine(List<BlnoPageRespDto> blnoPageRespDtoList) {
        for (BlnoPageRespDto blnoPageRespDto : blnoPageRespDtoList) {
            //是否在更正审批中
            if (Objects.equals(SiChangeStatusEnum.SI_AMENDMENT_PROCESSING.getValue(), blnoPageRespDto.getSiChangeStatus())) {
                blnoPageRespDto.setCanSplit(Boolean.FALSE);
                blnoPageRespDto.setCanCombine(Boolean.FALSE);
                continue;
            }
            if (Objects.equals(SiSplitCombineEnum.NORMAL.getValue(), blnoPageRespDto.getSplitCombineType())) {
                //可以拆分，可以合并
                blnoPageRespDto.setCanSplit(Boolean.TRUE);
                blnoPageRespDto.setCanCombine(Boolean.TRUE);
                continue;
            }
            if (Objects.equals(SiSplitCombineEnum.COMBINE.getValue(), blnoPageRespDto.getSplitCombineType())) {
                //可以拆分，不可以合并
                blnoPageRespDto.setCanSplit(Boolean.TRUE);
                blnoPageRespDto.setCanCombine(Boolean.FALSE);
                continue;
            }
            if (Objects.equals(SiSplitCombineEnum.NORMAL_SPLIT.getValue(), blnoPageRespDto.getSplitCombineType())
                    || Objects.equals(SiSplitCombineEnum.COMBINE_NORMAL.getValue(), blnoPageRespDto.getSplitCombineType())) {
                blnoPageRespDto.setCanSplit(Boolean.TRUE);
                blnoPageRespDto.setCanCombine(Boolean.FALSE);
                continue;
            }
            if (Objects.equals(SiSplitCombineEnum.SPLIT.getValue(), blnoPageRespDto.getSplitCombineType())) {
                blnoPageRespDto.setCanSplit(Boolean.FALSE);
                blnoPageRespDto.setCanCombine(Boolean.FALSE);
            }
        }

    }
    /**
     * 提单是否允许拆分
     * @param blnoDto 入参
     * @return Boolean
     */
    private Boolean isAllowSplit(BlnoPageRespDto blnoDto) {
        if (!CAN_SPLIT_AND_MERGE_SI_STATUS.contains(blnoDto.getSiStatus())) {
            return false;
        }
        //不可以拆分(更正审批中，拆分后子单)
        if (Objects.equals(SiChangeStatusEnum.SI_AMENDMENT_PROCESSING.getValue(), blnoDto.getSiChangeStatus())
                || Objects.equals(SiSplitCombineEnum.SPLIT.getValue(), blnoDto.getSplitCombineType())) {
            return false;
        }
        //可以拆分(原单，拆分审批中原始单)
        return Objects.equals(SiSplitCombineEnum.NORMAL.getValue(), blnoDto.getSplitCombineType())
                || Objects.equals(SiSplitCombineEnum.COMBINE.getValue(), blnoDto.getSplitCombineType())
                || Objects.equals(SiSplitCombineEnum.NORMAL_SPLIT.getValue(), blnoDto.getSplitCombineType())
                || Objects.equals(SiSplitCombineEnum.COMBINE_NORMAL.getValue(), blnoDto.getSplitCombineType());
    }

    /**
     * 提单是否允许合并
     * @param blnoDto 入参
     * @return Boolean
     */
    private Boolean isAllowCombine(BlnoPageRespDto blnoDto) {
        if (!CAN_SPLIT_AND_MERGE_SI_STATUS.contains(blnoDto.getSiStatus())) {
            return false;
        }
        if (!CAN_MERGE_CAGO_TYPES.contains(blnoDto.getCargoType())) {
            return false;
        }
        //不允许合并
        if (Objects.equals(SiChangeStatusEnum.SI_AMENDMENT_PROCESSING.getValue(), blnoDto.getSiChangeStatus())
                || Objects.equals(SiSplitCombineEnum.COMBINE.getValue(), blnoDto.getSplitCombineType())
                || Objects.equals(SiSplitCombineEnum.NORMAL_SPLIT.getValue(), blnoDto.getSplitCombineType())
                || Objects.equals(SiSplitCombineEnum.COMBINE_NORMAL.getValue(), blnoDto.getSplitCombineType())) {
            return false;
        }
        //允许合并
        return Objects.equals(SiSplitCombineEnum.NORMAL.getValue(), blnoDto.getSplitCombineType());
    }

    /**
     * 是否可以发起更正申请
     *
     * @param blno
     * @param blnoMap
     * @return
     */
    private Boolean checkCanUpdate(BlnoPageRespDto blno, Map<Long, BlnoDO> blnoMap) {
        Boolean updateFlag = true;
        List<String> siSpiltTypeList = Arrays.asList(SiSplitCombineEnum.NORMAL_SPLIT.getValue(), SiSplitCombineEnum.NORMAL_COMBINE.getValue(), SiSplitCombineEnum.COMBINE_NORMAL.getValue());
        if (siSpiltTypeList.contains(blno.getSignType())) {
            return false;
        }
        if (!Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue()
                , SiStatusEnum.AwaitingMaterialUploaded.getValue()
                , SiStatusEnum.BookingConfirmed.getValue()).contains(blno.getSiStatus())) {
            return false;
        }
        if (!blnoMap.isEmpty() && !Objects.nonNull(blnoMap.get(blno.getBlnoId()))) {
            return false;
        }
        return updateFlag;
    }

    /**
     * 批量根据订舱号查询订舱信息
     *
     * @param bookingNos 订舱号
     * @return Map<String, Long>
     */
    private Map<String, SoDO> querySoInfoByBookingNo(List<String> bookingNos) {
        Map<String, SoDO> soMap = new HashMap<>(16);
        List<SoDO> soList = soMapper.selectList(new LambdaQueryWrapperX<SoDO>()
                .inIfPresent(SoDO::getBookingNo, bookingNos)
                .eqIfPresent(SoDO::getSoStatus, BookingConfirmed.getValue())
                .eqIfPresent(SoDO::getValidStatus, ValidStatusEnum.DEFAULT_VALID.getValue())
        );

        if (CollectionUtils.isNotEmpty(soList)) {
            soMap = soList.stream().collect(Collectors.toMap(SoDO::getBookingNo, e -> e, (o1, o2) -> o1));
        }
        return soMap;
    }

    /**
     * 通过so批量查询船期数据
     *
     * @param map so信息
     * @return 船期数据map
     */
    private Map<Long, List<SoVslvoyDO>> querySoVslvoyInfo(Map<String, SoDO> map) {
        List<Long> soIdList = new ArrayList<>(map.values()).stream().map(SoDO::getId).filter(Objects::nonNull).collect(Collectors.toList());
        LambdaQueryWrapperX<SoVslvoyDO> queryModel = new LambdaQueryWrapperX<>();
        queryModel.in(SoVslvoyDO::getSoId, soIdList);
        List<SoVslvoyDO> soVslvoyDoList = soVslvoyMapper.selectList(queryModel);
        return soVslvoyDoList.stream().collect(Collectors.groupingBy(SoVslvoyDO::getSoId));
    }

    /**
     * 根据blnoIds查询发货信息
     *
     * @param blnoIds 提单id
     * @return Map<Long, List < BlnoScnRespDto>>
     */
    private Map<Long, List<BlnoScnRespDto>> queryBlnoScn(List<Long> blnoIds) {
        Map<Long, List<BlnoScnRespDto>> blnoScnMap = new HashMap<>(16);
        List<BlnoScnDO> blnoScns = blnoScnMapper.selectList(new LambdaQueryWrapperX<BlnoScnDO>()
                .in(BlnoScnDO::getBlnoId, blnoIds).eq(BlnoScnDO::getPartyTypeCode, PartyTypeCodeEnum.SHIPPER.getValue()));
        if (CollectionUtils.isNotEmpty(blnoScns)) {
            blnoScnMap = BeanUtil.copyToList(blnoScns, BlnoScnRespDto.class)
                    .stream()
                    .collect(Collectors.groupingBy(BlnoScnRespDto::getBlnoId));
        }

        return blnoScnMap;
    }

    /**
     * 根据blnoIds查询箱信息
     *
     * @param blnoIds 提单id
     * @return Map<Long, List < BlnoCntrUpdateReqVO>>
     */
    private Map<Long, List<BlnoCntrUpdateReqDto>> queryBlnoCntr(List<Long> blnoIds) {
        Map<Long, List<BlnoCntrUpdateReqDto>> blnoCntrMap = new HashMap<>(16);
        List<BlnoCntrDO> blnoCntrs = blnoCntrMapper.selectList(new LambdaQueryWrapperX<BlnoCntrDO>().in(BlnoCntrDO::getBlnoId, blnoIds));
        if (CollectionUtils.isNotEmpty(blnoCntrs)) {
            blnoCntrMap = BeanUtil.copyToList(blnoCntrs, BlnoCntrUpdateReqDto.class)
                    .stream()
                    .collect(Collectors.groupingBy(BlnoCntrUpdateReqDto::getBlnoId));
        }
        return blnoCntrMap;
    }

    @Override
    public CommonResult<List<ImExActualLoadingRespDto>> qryImpExActualLoadingList(ImExActualLoadingReqDto imExActualLoadingReqDto) {
        List<String> blNoList = imExActualLoadingReqDto.getBlNoList();
        List<BlnoInfoVO> siDetailListByBlNos = blnoService.getSiDetailListByBlNos(blNoList);
        //状态在SI确认之后的，从提单查询数据
        List<BlnoInfoVO> afterSiConfirm = Optional.ofNullable(siDetailListByBlNos).orElse(Collections.emptyList()).stream().filter(t -> AFTER_SI_CONFIRM.contains(t.getBlnoBasInfoVO().getSiStatus())).collect(Collectors.toList());

        //状态不在SI03-SI确认之后的，从SO查询数据
        ArrayList<String> otherBlNos = new ArrayList<>();
        otherBlNos.addAll(blNoList);
        if (CollectionUtils.isNotEmpty(afterSiConfirm)) {
            otherBlNos.removeAll(Optional.ofNullable(afterSiConfirm).orElse(Collections.emptyList()).stream().map(BlnoInfoVO::getBlnoBasInfoVO).map(BlnoBaseInfoVO::getBlNo).collect(Collectors.toList()));
        }
        List<SoDetailRespVO> soDetailListByBlNos = null;
        if (CollectionUtils.isNotEmpty(otherBlNos)) {
            soDetailListByBlNos = bizSoService.getSoDetailListByBlNos(otherBlNos);
        }

        List<ImExActualLoadingRespDto> imExActualLoadingRespDtos = new ArrayList<>();

        if (CollectionUtils.isNotEmpty(afterSiConfirm) || CollectionUtils.isNotEmpty(soDetailListByBlNos)) {
            if (CollectionUtils.isNotEmpty(afterSiConfirm)) {
                this.dealAfterSiConfirmData(afterSiConfirm, imExActualLoadingReqDto, imExActualLoadingRespDtos);
            }
            if (CollectionUtils.isNotEmpty(soDetailListByBlNos)) {
                this.dealBeforeSiConfirmData(soDetailListByBlNos, imExActualLoadingReqDto, imExActualLoadingRespDtos);
            }
        }
        return CommonResult.success(imExActualLoadingRespDtos);
    }

    private void dealAfterSiConfirmData(List<BlnoInfoVO> afterSiConfirm, ImExActualLoadingReqDto imExActualLoadingReqDto, List<ImExActualLoadingRespDto> imExActualLoadingRespDtos) {
        String imExType = imExActualLoadingReqDto.getImExType();
        boolean isIm = "IM".equals(imExType);
        boolean isEx = "EX".equals(imExType);
        String vesselCode = imExActualLoadingReqDto.getVesselCode();
        boolean isEmptyVesselCode = StringUtils.isEmpty(vesselCode);
        String voyageEx = imExActualLoadingReqDto.getVoyageEx();
        boolean isEmptyVoyageEx = StringUtils.isEmpty(voyageEx);
        afterSiConfirm.stream()
                .filter(t -> t.getBlnoVslvoys().stream()
                        .anyMatch(v ->
                                (isIm && imExActualLoadingReqDto.getPortCode().equals(v.getPodCode()) && (isEmptyVesselCode || vesselCode.equals(v.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(v.getVoyageIm())))
                                        || (isEx && imExActualLoadingReqDto.getPortCode().equals(v.getPolCode()) && (isEmptyVesselCode || vesselCode.equals(v.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(v.getVoyageEx())))
                        )).forEach(t -> {
                    ImExActualLoadingRespDto imExActualLoadingRespDto = BeanUtil.copyProperties(t.getBlnoBasInfoVO(), ImExActualLoadingRespDto.class);

                    List<BlnoVslvoyUpdateReqVO> blVslvoys = Optional.ofNullable(t.getBlnoVslvoys()).orElse(Collections.emptyList()).stream()
                            .filter(blVslvoy ->
                                    (isIm && imExActualLoadingReqDto.getPortCode().equals(blVslvoy.getPodCode()) && (isEmptyVesselCode || vesselCode.equals(blVslvoy.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(blVslvoy.getVoyageIm())))
                                            || (isEx && imExActualLoadingReqDto.getPortCode().equals(blVslvoy.getPolCode()) && (isEmptyVesselCode || vesselCode.equals(blVslvoy.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(blVslvoy.getVoyageEx())))
                            ).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(blVslvoys)) {
                        imExActualLoadingRespDto.setVslvoy(BeanUtil.copyProperties(blVslvoys.get(0), ImExActualLoadingVslvoyRespDto.class));
                    }
                    imExActualLoadingRespDto.setCargo(BeanUtil.copyProperties(Optional.ofNullable(t.getBlnoCargos()).orElse(Collections.emptyList()).get(0), ImExActualLoadingCargoRespDto.class));
                    List<BlnoCntrUpdateReqVO> blnoCntrs = t.getBlnoCntrs();
                    if (CollectionUtils.isNotEmpty(blnoCntrs)) {
                        List<ImExActualLoadingCntrRespDto> imExActualLoadingCntrRespDtos = BeanUtil.copyToList(blnoCntrs, ImExActualLoadingCntrRespDto.class);
                        imExActualLoadingCntrRespDtos.stream().forEach(cntr -> {
                            cntr.setCntrQuantity(1);
                            cntr.setCargoQuantity(cntr.getQuantity());
                        });
                        imExActualLoadingRespDto.setCntrList(imExActualLoadingCntrRespDtos);
                    }
                    List<BlnoScnUpdateReqVO> blnoScns = t.getBlnoScns();
                    if (CollectionUtils.isNotEmpty(blnoScns)) {
                        imExActualLoadingRespDto.setScnList(BeanUtil.copyToList(blnoScns, ImExActualLoadingScnRespDto.class));
                    }

                    imExActualLoadingRespDtos.add(imExActualLoadingRespDto);
                });
    }

    private void dealBeforeSiConfirmData(List<SoDetailRespVO> soDetailListByBlNos, ImExActualLoadingReqDto imExActualLoadingReqDto, List<ImExActualLoadingRespDto> imExActualLoadingRespDtos) {
        String imExType = imExActualLoadingReqDto.getImExType();
        boolean isIm = "IM".equals(imExType);
        boolean isEx = "EX".equals(imExType);
        String vesselCode = imExActualLoadingReqDto.getVesselCode();
        boolean isEmptyVesselCode = StringUtils.isEmpty(vesselCode);
        String voyageEx = imExActualLoadingReqDto.getVoyageEx();
        boolean isEmptyVoyageEx = StringUtils.isEmpty(voyageEx);
        soDetailListByBlNos.stream()
                .filter(t -> t.getSoVslvoyList().stream()
                        .anyMatch(v ->
                                (isIm && imExActualLoadingReqDto.getPortCode().equals(v.getPodCode()) && (isEmptyVesselCode || vesselCode.equals(v.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(v.getVoyageIm())))
                                        || (isEx && imExActualLoadingReqDto.getPortCode().equals(v.getPolCode()) && (isEmptyVesselCode || vesselCode.equals(v.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(v.getVoyageEx())))
                        ))
                .forEach(t -> {
                    ImExActualLoadingRespDto imExActualLoadingRespDto = BeanUtil.copyProperties(t.getSo(), ImExActualLoadingRespDto.class);

                    List<SoVslvoyRespVO> soVslvoys = Optional.ofNullable(t.getSoVslvoyList()).orElse(Collections.emptyList()).stream()
                            .filter(soVslvoy -> (isIm && imExActualLoadingReqDto.getPortCode().equals(soVslvoy.getPodCode()) && (isEmptyVesselCode || vesselCode.equals(soVslvoy.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(soVslvoy.getVoyageIm())))
                                    || (isEx && imExActualLoadingReqDto.getPortCode().equals(soVslvoy.getPolCode()) && (isEmptyVesselCode || vesselCode.equals(soVslvoy.getVesselCode())) && (isEmptyVoyageEx || voyageEx.equals(soVslvoy.getVoyageEx()))))
                            .collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(soVslvoys)) {
                        imExActualLoadingRespDto.setVslvoy(BeanUtil.copyProperties(soVslvoys.get(0), ImExActualLoadingVslvoyRespDto.class));
                    }
                    List<SoCargoRespVO> soCargoList = t.getSoCargoList();
                    if (CollectionUtils.isNotEmpty(soCargoList)) {
                        imExActualLoadingRespDto.setCargo(BeanUtil.copyProperties(soCargoList.get(0), ImExActualLoadingCargoRespDto.class));
                    }
                    List<SoForecastRespVO> soForecastList = t.getSoForecastList();
                    if (CollectionUtils.isNotEmpty(soForecastList)) {
                        List<ImExActualLoadingCntrRespDto> imExActualLoadingCntrRespDtos = BeanUtil.copyToList(soForecastList, ImExActualLoadingCntrRespDto.class);
                        imExActualLoadingRespDto.setCntrList(imExActualLoadingCntrRespDtos);
                    }
                    if (!ObjectUtils.isEmpty(t.getSoScn())) {
                        imExActualLoadingRespDto.setScnList(Arrays.asList(BeanUtil.copyProperties(t.getSoScn(), ImExActualLoadingScnRespDto.class)));
                    }
                    imExActualLoadingRespDtos.add(imExActualLoadingRespDto);
                });
    }

    @Override
    public CommonResult<BlnoSplitCombineRespDto> getSplitCombineAuditDetail(String type, Long blnoId) {

        //提单拆分或者合并
        if (Objects.equals(type, BlnoSpiltCombineTypeEnum.SPLIT.getValue()) || Objects.equals(type, BlnoSpiltCombineTypeEnum.COMBINE.getValue())) {
            BlnoSplitCombineRespDto blnoSplitCombineDetail = getBlnoSplitCombineDetail(blnoId);
            return CommonResult.success(blnoSplitCombineDetail);
        }
        //更正流程
        if (Objects.equals(type, DocApiConstants.SI_CHANGE_TYPE)) {
            BlnoSplitCombineRespDto respDto = new BlnoSplitCombineRespDto();
            //查询新单
            BlnoDO newBlnoDO = blnoMapper.selectOne(new LambdaQueryWrapperX<BlnoDO>().eq(BlnoDO::getOldBlnoId, blnoId)
                            .orderByDesc(BlnoDO::getCreateTime)
                            .last("LIMIT 1"));
            if (Objects.nonNull(newBlnoDO)) {
                BlnoDetailReqDto oldReqDto = new BlnoDetailReqDto();
                oldReqDto.setBlnoId(newBlnoDO.getId());
                BlnoInfoDto newBlnoInfo = getBlnoInfo(newBlnoDO.getId());
                respDto.setNewBlnoInfo(newBlnoInfo);
            }
            //查询原单
            BlnoDetailReqDto oldReqDto = new BlnoDetailReqDto();
            oldReqDto.setBlnoId(blnoId);
            BlnoInfoDto oldBlnoInfo = getBlnoInfo(blnoId);
            respDto.setOldBlnoInfo(oldBlnoInfo);
            //查询审核记录
            ConfirmNoticeDO confirmNotice = confirmNoticeMapper.selectOne(new LambdaQueryWrapperX<ConfirmNoticeDO>().eq(ConfirmNoticeDO::getBlnoId, newBlnoDO.getId()).last("LIMIT 1"));
            if (Objects.isNull(confirmNotice)) {
                return CommonResult.success(null);
            }
            SoSplitCombineRespDto auditResult = new SoSplitCombineRespDto();
            auditResult.setApplyTime(confirmNotice.getApplyTime());
            auditResult.setApplyTimeString(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(confirmNotice.getApplyTime()));
            auditResult.setApplyUser(confirmNotice.getApplyUser());
            auditResult.setOperationType(DocApiConstants.SI_CHANGE_TYPE);
            auditResult.setOperationNo(confirmNotice.getId() + "");
            auditResult.setApproveStatus(confirmNotice.getStatus());
            if (ConfirmNoticeStatusEnum.REJECTED.getValue().equals(confirmNotice.getStatus())) {
                List<ConfirmNoticeDetailDO> confirmNoticeList = confirmNoticeDetailMapper.selectList(new LambdaQueryWrapperX<ConfirmNoticeDetailDO>().eq(ConfirmNoticeDetailDO::getNoticeId, confirmNotice.getId()));
                if (CollectionUtils.isNotEmpty(confirmNoticeList)) {
                    confirmNoticeList.forEach(entity -> {
                        if (ConfirmNoticeStatusEnum.REJECTED.getValue().equals(entity.getNoticeStatus())) {
                            auditResult.setApproveStatus(entity.getNoticeStatus());
                            auditResult.setApproveStatus(ConfirmNoticeStatusEnum.REJECTED.getValue());
                            auditResult.setRemark(entity.getApproveRemark());
                            auditResult.setApproveTime(entity.getApproveTime());
                            auditResult.setApproveUser(entity.getApproveUser());
                            auditResult.setApproveTimeString(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(entity.getApproveTime()));
                        }
                    });
                }
            }
            respDto.setAuditResult(auditResult);
            return CommonResult.success(respDto);
        }
        return null;

    }

    /**
     * 查询提单记录信息
     *
     * @param blnoId
     * @return
     */
    private BlnoInfoDto getBlnoInfo(Long blnoId) {
        BlnoInfoDto blnoInfoDto = new BlnoInfoDto();
        BlnoInfoVO blnoInfoVO = blnoService.getSiDetail(blnoId);
        //基础信息
        BlnoBaseInfoDto blnoBaseInfo = BlnoConvert.INSTANCE.convertBlnoBaseInfoDto(blnoInfoVO.getBlnoBasInfoVO());
        SoDO soDO = soMapper.selectOne(new LambdaQueryWrapper<SoDO>()
                .eq(SoDO::getBlNo, blnoBaseInfo.getBlNo())
                .eq(SoDO::getBookingNo, blnoBaseInfo.getBookingNo())
                .last("limit 1")
        );
        if (soDO != null) {
            blnoBaseInfo.setSoId(soDO.getId());
        }

        blnoInfoDto.setBlnoBasInfoVO(blnoBaseInfo);
        //换单数据
       /* try {
            BlnoImPageReqVO reqVO = new BlnoImPageReqVO();
            reqVO.setBlNo(blnoBaseInfo.getBlNo());
            List<BlnoImPageRespVO> exportList = blnoService.exportBlnoIm(reqVO);
            if (CollectionUtil.isNotEmpty(exportList)) {
                blnoInfoDto.getBlnoBasInfoVO().setDoDate(exportList.get(exportList.size() - 1).getDoDate());
            }
        } catch (Exception e) {
            log.error("查询换单数据异常", e);
        }*/
        //航线信息
        List<BlnoVslvoyUpdateReqDto> blnoVslvoys = BlnoVslvoyConvert.INSTANCE.convertBlnoVslvoyUpdateReqDto(blnoInfoVO.getBlnoVslvoys());
        blnoService.setShippingScheduleTime(blnoVslvoys);
        blnoInfoDto.setBlnoVslvoys(blnoVslvoys);
        //支付信息
        List<BlnoPaymentUpdateReqDto> blnoPayments = BlnoPaymentConvert.INSTANCE.convertBlnoPaymentUpdateReqDto(blnoInfoVO.getBlnoPayments());
        blnoInfoDto.setBlnoPayments(blnoPayments);
        //附加要求
        List<RequirementsUpdateReqDto> reqList = RequirementsConvert.INSTANCE.convertRequirementsUpdateReqVO(blnoInfoVO.getReqVOList());
        blnoInfoDto.setReqVOList(reqList);
        //额外要求
        List<AttachmentUpdateReqDto> attachmentReqs = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqVO(blnoInfoVO.getAttachmentReqVO());
        blnoInfoDto.setAttachmentReqVO(attachmentReqs);
        //常用收发通
        List<BlnoScnUpdateReqDto> blnoScnUpdateReqs = BlnoScnConvert.INSTANCE.convertBlnoScnUpdateReqDtoVo(blnoInfoVO.getBlnoScns());
        blnoInfoDto.setBlnoScns(blnoScnUpdateReqs);
        //箱信息
        List<BlnoCntrUpdateReqDto> blnoCntrs = BlnoCntrConvert.INSTANCE.convertBlnoCntrUpdateReqDto(blnoInfoVO.getBlnoCntrs());
        blnoInfoDto.setBlnoCntrs(blnoCntrs);

        //货物信息
        List<BlnoCargoUpdateReqDto> blnoCargos = BlnoCargoConvert.INSTANCE.convertBlnoCargoUpdateReqDto(blnoInfoVO.getBlnoCargos());
        blnoInfoDto.setBlnoCargos(blnoCargos);

        return blnoInfoDto;
    }

    /**
     * 获取最新的拆分或者合并的审核记录详情
     *
     * @param blnoId
     * @return
     */
    private BlnoSplitCombineRespDto getBlnoSplitCombineDetail(Long blnoId) {

        BlnoSplitCombineRespDto respDto = new BlnoSplitCombineRespDto();
        // 审核结果获取
        BlnoSpiltCombineDO blnoSpiltCombineDo = getSpiltCombineAudit(blnoId);
        if (blnoSpiltCombineDo == null) {
            return respDto;
        }
        SoSplitCombineRespDto auditResult = BlnoSpiltCombineConvert.INSTANCE.convertToDto(blnoSpiltCombineDo);
        respDto.setAuditResult(auditResult);
        auditResult.setRemark(blnoSpiltCombineDo.getApproveReason());
        // 提单号获取
        List<BlnoSpiltCombineDetailDO> list = blnoSpiltCombineDetailMapper.selectList(BlnoSpiltCombineDetailDO::getOperationId, blnoSpiltCombineDo.getId());
        if (CollectionUtil.isEmpty(list)) {
            return respDto;
        }
        // 拆取的是newBlnoId
        if (Objects.equals(blnoSpiltCombineDo.getOperationType(), BlnoSpiltCombineTypeEnum.SPLIT.getValue())) {
            List<Long> blnoIdLiSt = list.stream().map(BlnoSpiltCombineDetailDO::getNewBlnoId).collect(Collectors.toList());
            respDto.setBlnoIdList(blnoService.getBlNoByIdListNotDependOnDeleted(blnoIdLiSt));
        }
        // 合并取的是blnoId
        if (Objects.equals(blnoSpiltCombineDo.getOperationType(), BlnoSpiltCombineTypeEnum.COMBINE.getValue())) {
            List<Long> blnoIdLiSt = list.stream().map(BlnoSpiltCombineDetailDO::getBlnoId).collect(Collectors.toList());
            respDto.setBlnoIdList(blnoService.getBlNoByIdListNotDependOnDeleted(blnoIdLiSt));
            // 得到合并主单的blNo
            List<String> blNoList = blnoService.getBlNoByIdListNotDependOnDeleted(Collections.singletonList(list.get(0).getNewBlnoId()));
            if (CollectionUtils.isNotEmpty(blNoList)) {
                auditResult.setBlNo(blNoList.get(0));
            }
        }
        return respDto;
    }

    /**
     * 获取最新的拆分或者拆并的审核记录
     *
     * @param blnoId
     * @return
     */
    private BlnoSpiltCombineDO getSpiltCombineAudit(Long blnoId) {
        // 拆并详情表数据 取最新的那条
        BlnoSpiltCombineDetailDO detailDO = blnoSpiltCombineDetailMapper.selectOne(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>()
                .eq(BlnoSpiltCombineDetailDO::getBlnoId, blnoId)
                .orderByDesc(BlnoSpiltCombineDetailDO::getUpdateTime)
                .last("limit 1"));
        if (detailDO == null) {
            return null;
        }
        return blnoSpiltCombineMapper.selectById(detailDO.getOperationId());
    }

    /**
     * 提交拼箱校验，提交不能包含拼箱
     *
     * @param blnoCntrs     提单箱信息
     * @param bkgVesselCode 船名
     * @param bkgVoyage     航次
     */
    private void lclCheck(List<BlnoCntrUpdateReqVO> blnoCntrs, String bkgVesselCode, String bkgVoyage) {

        Long blnoId = blnoCntrs.get(0).getBlnoId();
        //查询同船名航次数据集装箱，非草稿状态，目的港提单标记为true
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>().eqIfPresent(BlnoDO::getBkgVesselCode, bkgVesselCode)
                .eqIfPresent(BlnoDO::getBkgVoyage, bkgVoyage)
                .eqIfPresent(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                .eqIfPresent(BlnoDO::getPodBlFlag, true)
                .neIfPresent(BlnoDO::getSiStatus, SiStatusEnum.BookingDraft.getValue())
                .neIfPresent(BlnoDO::getId, blnoId));
        if (CollectionUtils.isEmpty(blnoDos)) {
            return;
        }
        List<Long> blnoIds = blnoDos.stream().map(BlnoDO::getId).collect(Collectors.toList());
        //提交单的箱
        Set<String> cntrNos = blnoCntrs.stream().map(BlnoCntrUpdateReqVO::getCntrNo).collect(Collectors.toSet());
        List<BlnoCntrDO> lclCntrList = blnoCntrMapper.selectList(new LambdaQueryWrapperX<BlnoCntrDO>()
                .inIfPresent(BlnoCntrDO::getBlnoId, blnoIds)
                .inIfPresent(BlnoCntrDO::getCntrNo, cntrNos));
        if (CollectionUtils.isNotEmpty(lclCntrList)) {
            String errorMsg = lclCntrList.stream().map(BlnoCntrDO::getCntrNo).collect(Collectors.joining("|"));
            throw ServiceExceptionUtil.exception(BlnoCntrErrorCodeConstants.BLNO_CNTR_EXITSTS, errorMsg);
        }

    }
    @Override
    public CommonResult<List<BlnoVesselVesselPortRespDto>> queryBlnoInfoByVesselVesselPort(@Valid BlnoVesselVesselPortReqDto blnoVesselVesselPortReqDto) {

        List<BlnoVesselVesselPortRespDto> blnoVesselVesselPortRespDtos = blnoMapper.queryBlnoInfoByVesselVesselPort(blnoVesselVesselPortReqDto);

        if(blnoVesselVesselPortRespDtos.isEmpty()){
            return CommonResult.success(blnoVesselVesselPortRespDtos);
        }
        List<Long> blnoIds = blnoVesselVesselPortRespDtos.stream().map(BlnoVesselVesselPortRespDto::getBlnoId).collect(Collectors.toList());
        //发货人信息
        Map<Long, List<BlnoScnRespDto>> blnoScnMap = queryBlnoScn(blnoIds);
        //箱信息
        Map<Long, List<BlnoCntrUpdateReqDto>> blnoCntrsMap = queryBlnoCntr(blnoIds);
        for (BlnoVesselVesselPortRespDto entity : blnoVesselVesselPortRespDtos) {

            //箱信息
            entity.setBlnoCntrs(BeanUtil.copyToList(blnoCntrsMap.getOrDefault(entity.getBlnoId(), Collections.emptyList()), BlnoVesselVesselPortCntrRespDto.class));
            for (BlnoVesselVesselPortCntrRespDto blnoCntr : entity.getBlnoCntrs()) {
                blnoCntr.setCntrStatus(entity.getCntrStatus());
            }
            //发货人信息
            entity.setBlnoScns(BeanUtil.copyToList(blnoScnMap.getOrDefault(entity.getBlnoId(), Collections.emptyList()), BlnoVesselVesselPortScnRespDto.class));
            //航线信息
            entity.setBlnoVslvoys(
                    BeanUtil.copyToList(blnoVslvoyService.getBlnoVslvoyBySiId(entity.getBlnoId()), BlnoVesselVesselPortVslvoyRespDto.class));
        }
        return CommonResult.success(blnoVesselVesselPortRespDtos);
    }


}





